package cz.acies.uniql.sql.element;

import java.util.ArrayList;

import org.dom4j.Element;
import org.dom4j.dom.DOMElement;


import cz.acies.uniql.dialect.Dialect;
import cz.acies.uniql.sql.SelectQuery;
import cz.acies.uniql.sql.UniQLExpr;

public class UniQLWhere extends UniQLExpr 
{	
	private static final long serialVersionUID = 7609470616776816279L;
	
	private UniQLCriterion criterions;
	
	/**
	 * Konstruktor tridy UniQLWhere.
	 */
	public UniQLWhere() {
		super();
	}

	/**
	 * Konstruktor tridy UniQLWhere.
	 * 
	 * @param dialect - dialect
	 */
	public UniQLWhere(Dialect dialect) {
		super(dialect);
	}

	/**
	 * Konstruktor vytvori nonou instanci objektu UniQLWhere s vlastnostmi
	 * objektu zadaneho jako parametr konstruktoru.
	 *  
	 * @param orderBy
	 */
	public UniQLWhere(UniQLWhere expr) throws Exception {
		this();
		UniQLWhere clone = (UniQLWhere) expr.clone();
		
		setId(clone.getId());
		setName(clone.getName());
		setDialect(clone.getDialect());
		setAliasMap(clone.getAliasMap());

		setCriterions((clone.getCriterions() != null) ? clone.getCriterions() : null);
	}

	// -----  -----------------------------------------------------------------
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#translate()
	 */
	public String translate() throws Exception {
		StringBuffer sb = new StringBuffer();
		if (criterions != null) {
			if (!criterions.isEmpty()) {
				sb.append("WHERE ");
				criterions.setUniQLProperties(this);
				sb.append(criterions.translate());
			}
		}
		return sb.toString();
	}
	
	/**
	 * Metoda vymaze vsechny doposud pridane podmiky.
	 */
	public UniQLWhere clear() {
		if (criterions != null) criterions.clear();
		criterions = null;
		return this;
	}
	
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	@Override
	public Object clone() throws CloneNotSupportedException {
		Object clone = super.clone();
		assert clone instanceof UniQLWhere;
		((UniQLWhere) clone).setCriterions((getCriterions() != null) ? (UniQLCriterion)criterions.clone() : null);
		return clone;
	}

	// -----  -----------------------------------------------------------------

	/**
	 * Toto�n� metoda s addCondition(UniQLCondition cf)
	 * 
	 * @param condition - condition
	 * 
	 * @return vraci objekt typu UniQLWhere
	 */
	public UniQLWhere add(UniQLCondition cf) {
		return addCondition(cf);
	}

	/**
	 * Prida novy objekt condition. U teto metody je mezi pridavany condition a 
	 * pripadny dalsi pridavany condition, automaticky dolnen operator AND.   
	 * 
	 * @param condition - condition
	 * 
	 * @return vraci objekt typu UniQLWhere
	 */
	public UniQLWhere addCondition(UniQLCondition cf) {
		if (this.criterions == null) this.criterions = new UniQLCriterion();
		this.criterions.addCondition(cf);
		return this;
	}
	
	// -----  -----------------------------------------------------------------

	/**
	 * Toto�n� metoda s addCondition(UniQLCondition cf, String op)
	 * 
	 * @param condition - condition
	 * @param op - op
	 * 
	 * @return vraci objekt typu UniQLCriterion
	 */
	public UniQLWhere add(UniQLCondition cf, String op) {
		return addCondition(cf, op);
	}

	/**
	 * Prida novy objekt condition a oprerator, ktery bude ve vyslednem dotazu za
	 * pridavanou podminkou.
	 * 
	 * @param condition - condition
	 * @param op - op
	 * 
	 * @return vraci objekt typu UniQLCriterion
	 */
	public UniQLWhere addCondition(UniQLCondition cf, String op) {
		if (this.criterions == null) this.criterions = new UniQLCriterion();
		this.criterions.addCondition(cf, op);
		return this;
	}

	/**
	 * Prida novy objekt condition a oprerator, ktery bude ve vyslednem dotazu za
	 * pridavanou podminkou a operator, ktery bude ve vyslednem dotazu pred
	 * pridavanou podminkou. 
	 * 
	 * @param op1 - op1
	 * @param condition - condition
	 * @param op2 - op2
	 * 
	 * @return vraci objekt typu UniQLWhere
	 */
	public UniQLWhere addCondition(String op1, UniQLCondition cf, String op2) {
		this.add(op1, cf);
		this.setLastConditionOperand(op2);
		return this;
	}

	/**
	 * Metoda schodna s addCondition(String op1, UniQLCondition cf, String op2)
	 * 
	 */
	public UniQLWhere add(String op1, UniQLCondition cf, String op2) {
		this.add(op1, cf);
		this.setLastConditionOperand(op2);
		return this;
	}

	/**
	 * Toto�n� metoda s addCondition(UniQLCondition cf, int op)
	 * 
	 * @param condition - condition
	 * @param op - op
	 * 
	 * @return vraci objekt typu UniQLCriterion
	 */
	
	public UniQLWhere add(UniQLCondition cf, int op) {
		return addCondition(cf, op);
	}

	/**
	 * Prida novy objekt condition a oprerator, ktery bude ve vyslednem dotazu za
	 * pridavanou podminkou.
	 * 
	 * @param condition - condition
	 * @param op - op
	 * 
	 * @return vraci objekt typu UniQLCriterion
	 */
	
	public UniQLWhere addCondition(UniQLCondition cf, int op) {
		if (this.criterions == null) this.criterions = new UniQLCriterion();
		String operator = "";
		switch (op) {
			case 0: operator = "AND"; break;
			case 1: operator = "OR"; break;
			default: break;
		}
		this.criterions.addCondition(cf, operator);
		return this;
	}

	/**
	 * Toto�n� metoda s addCondition(String op, UniQLCondition cf)
	 * 
	 * @param condition - condition
	 * @param op - op
	 * 
	 * @return vraci objekt typu UniQLCriterion
	 */
	public UniQLWhere add(String op, UniQLCondition cf) {
		return addCondition(op, cf);
	}

	/**
	 * Prida novy objekt condition a oprerator, ktery bude ve vyslednem dotazu p�ed
	 * pridavanou podminkou.
	 * 
	 * @param condition - condition
	 * @param op - op
	 * 
	 * @return vraci objekt typu UniQLWhere
	 */
	public UniQLWhere addCondition(String op, UniQLCondition cf) {
		if (this.criterions == null) this.criterions = new UniQLCriterion();
		this.setLastConditionOperand(op);
		this.criterions.addCondition(cf);
		return this;
	}

	/**
	 * Toto�n� metoda s addCondition(UniQLConditionGroup cf, String op)
	 * @param condition - condition
	 * @param op - op
	 * 
	 * @return vraci objekt typu UniQLWhere
	 */
	public UniQLWhere add(UniQLConditionGroup cf, String op) {
		return addCondition(cf, op);
	}

	/**
	 * Prida novy objekt conditionGroup a oprerator, ktery bude ve vyslednem dotazu za
	 * pridavanou podminkou.
	 * 
	 * @param condition - condition
	 * @param op - op
	 * 
	 * @return vraci objekt typu UniQLCriterion
	 */
	public UniQLWhere addCondition(UniQLConditionGroup cf, String op) {
		if (this.criterions == null) this.criterions = new UniQLCriterion();
		this.criterions.addCondition(cf, op);
		return this;
	}

	/**
	 * Zada hodnotu operatoru pro posledne zadanou podminku.
	 * 
	 * @param op nova hodnota operatoru
	 */
	public UniQLWhere setLastConditionOperand(String op) {
		if (this.criterions != null) {
			this.criterions.setLastConditionOperator(op);
		}
		return this;
	}
	
	// -----  -----------------------------------------------------------------
	
	/**
	 * Pokud je kontejner criterions prazdny vraci true.
	 * 
	 * @return - vraci true, pokud je kontejner criterions prazdny
	 */
	public boolean isEmpty() {
		if (criterions == null) return true;
		return criterions.isEmpty();
	}

	// -----  -----------------------------------------------------------------
	
	/**
	 * Navraci kontejner criterions.
	 * 
	 * @return vraci kontejner criterions (objekt typu ArrayList)
	 */
	public UniQLCriterion getCriterions() {
		return criterions;
	}

	/**
	 * Nahradi stavajici criterions za novou.
	 * 
	 * @param novy kontejner criterions
	 */
	public void setCriterions(UniQLCriterion criterions) {
		this.criterions = criterions;
	}

	/**
	 * P�id� ke stavajicim podm�nk�m nov�, z obsa�en� v parametru criterions.
	 * 
	 * @param kontejner criterions obsahuj�c� nov� podm�nky.  
	 */
	public void addCriterions(UniQLCriterion c) {
		addCriterions(c, null);
	}

	/**
	 * Metoda shodn� s addCriterions(UniQLCriterion c) 
	 * 
	 * @param c
	 */
	public void add(UniQLCriterion c) {
		addCriterions(c);
	}
	
	/**
	 * P�id� ke stavajicim podm�nk�m nov�, z obsa�en� v parametru criterions a 
	 * n�sledn� nastav� LastConditionOperator na parametr operator. 
	 * 
	 * @param c
	 * @param op
	 */
	public void addCriterions(UniQLCriterion c, String op) {
		if (c == null) return;
		try {
			if (c instanceof UniQLConditionGroup) {
				addCondition((UniQLConditionGroup)c, op);
			} else {				
				ArrayList<UniQLCriterionElement> list = c.getConditions();
				int sz = list.size();
				for (int i = 0; i < sz; i++) {
					UniQLCriterionElement element = list.get(i);
					int oparetor = element.getOperator();
					Object obj = element.getCondition();
					if (obj instanceof UniQLCondition) {						
						UniQLCondition condition = (UniQLCondition)element.getCondition();
						this.addCondition(condition, oparetor);
					} else {
						UniQLConditionGroup condition = (UniQLConditionGroup)element.getCondition();
						String o = (oparetor == 0) ? "AND" : "OR";
						this.add(condition, o);
					}
				}
				if (op != null) this.setLastConditionOperand(op);			
			}
		} catch (Exception exc) {
			exc.printStackTrace();
		}
	}

	/**
	 * Metoda shodn� s addCriterions(UniQLCriterion c, String op)
	 * 
	 * @param c
	 * @param op
	 */
	public void add(UniQLCriterion c, String op) {
		addCriterions(c, op);
	}
	
	/**
	 * Metoda nejprve nastav� LastConditionOperator na parametr operator ve st�vj�c�m 
	 * seznamu podm�nek a pak p�id� dal�� podm�nky odsa�en� s objectu UniQLCrierion.
	 * 
	 * @param op
	 * @param c
	 */
	public void addCriterions(String op, UniQLCriterion c) {
		if (op != null) this.setLastConditionOperand(op);
		addCriterions(c);
	}

	/**
	 * Metoda shodn� s addCriterions(String op, UniQLCriterion c)
	 * 
	 * @param op
	 * @param c
	 */
	public void add(String op, UniQLCriterion c) {
		addCriterions(op, c);
	}
	
	/**
	 * Metoda nejprve nastavi LastConditionOperator na parametr operator1 ve stavjicim 
	 * seznamu podminek a pak prida dalsi podminky obsazene s objectu UniQLCrierion a 
	 * nakonec na posledn� podmince v seznamu nastavi LastConditionOperator na parametr
	 * operator2.
	 * 
	 * @param op1
	 * @param c
	 * @param op2
	 * 
	 * @return UniQLWhere
	 */
	public UniQLWhere addWhereCriterion(String op1, UniQLCriterion c, String op2) {
		this.addCriterions(op1, c);
		this.setLastConditionOperand(op2);
		return this;
	}
	
	/**
	 * Metoda shodn� jako addWhereCriterion (String op1, UniQLCriterion c, String op2) 
	 */
	public UniQLWhere add(String op1, UniQLCriterion c, String op2) {
		return addWhereCriterion(op1, c, op2);
	}

	// -----  -----------------------------------------------------------------
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#transToXML()
	 */
	public String transToXML() throws Exception {
		DOMElement node = new DOMElement("UniQLWhere");
		transToXML(node);
		return node.asXML();
	}
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#transToXML(org.dom4j.dom.DOMElement)
	 */
	public DOMElement transToXML(DOMElement node) throws Exception {
		node.setName("UniQLWhere");
		if (criterions != null) {
			if (criterions.getConditions().size() > 0) {
				DOMElement childs = criterions.transToXML(new DOMElement("UniQLExpr"));
				node.appendChild(childs);
			}
		}
		return node;
	}
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#xmlToExpr(java.lang.String)
	 */
	public UniQLExpr xmlToExpr(String xml) throws Exception {
		//System.out.println("UniQLWhere.xmlToExpr(xml)");
		return null;
	}

	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#xmlToExpr(org.dom4j.Element)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public UniQLExpr xmlToExpr(Element node) throws Exception {
		ArrayList<Element> params = new ArrayList<Element>(node.elements());
		for (int i = 0; i < params.size(); i++) {
			Element child = (Element)params.get(i);
			String nodename = child.getName();
			UniQLExpr expr = null;
			if (nodename.trim().equals("UniQLCriterion")) {
				expr = new UniQLCriterion();
				expr.xmlToExpr(child);
				if (expr != null) {
					this.setCriterions((UniQLCriterion)expr);
				}				
			}
		}
		return this;
	}

	// -----  -----------------------------------------------------------------
	
	public String toString() {
		Dialect dialect = this.getDialect();
		String schema = "";
		if (dialect != null && dialect.getSchema() != null) schema = dialect.getSchema();
		return "UniQLWhere( criterions="+criterions+" ), schema = "+schema+".";
	}
}
